home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / timer / ds5000.md / timerMC.c < prev    next >
C/C++ Source or Header  |  1991-03-05  |  16KB  |  583 lines

  1. /*
  2.  * timerMC.c --
  3.  *
  4.  *    This file contains routines that manipulate the MC 146818 real-time
  5.  *    clock.
  6.  *
  7.  *    For a detailed explanation of the chip, see the "PMAX Desktop
  8.  *    Workstation Functional Specification, Revision 1.1" pages 62-66.
  9.  *
  10.  * Copyright (C) 1989 Digital Equipment Corporation.
  11.  * Permission to use, copy, modify, and distribute this software and
  12.  * its documentation for any purpose and without fee is hereby granted,
  13.  * provided that the above copyright notice appears in all copies.  
  14.  * Digital Equipment Corporation makes no representations about the
  15.  * suitability of this software for any purpose.  It is provided "as is"
  16.  * without express or implied warranty.
  17.  */
  18.  
  19. /*
  20.  * This chip has a real-time clock (RTC) and an interval timer.  The real-time
  21.  * clock is basically useless because it only has a resolution of seconds.
  22.  * The machine-independent part assumes that we have a free-running counter
  23.  * which has a resolution of milliseconds.  What we end up doing is
  24.  * faking the free running counter. The RTC generates an interrupt every
  25.  * second which we use to increment the seconds counter and clear the
  26.  * microseconds counter.  The interval timer is used to increment the
  27.  * microseconds counter.  We have a problem when interrupts are turned off
  28.  * since the counter doesn't get incremented.  This is fixed by comparing
  29.  * the difference in the hardware clock since the last interrupt against
  30.  * the difference in the software clock.  The hardware clock is supposed
  31.  * to go invalid 244 microseconds after the interrupt.  It isn't clear
  32.  * when the update-in-progress flag is set. My reading of the
  33.  * manual leads me to believe that it is set at the same time as the
  34.  * interrupt.  In this were true we could never read the hardware clock
  35.  * in the interrupt handler.  Either this isn't true, or we are usually
  36.  * delayed enough getting to the handler so that the clock is valid 
  37.  * again because the update-in-progress bit is never set (I ran a few
  38.  * tests).  
  39.  */
  40.  
  41. #ifndef lint
  42. static char rcsid[] = "$Header: /sprite/src/kernel/timer/ds5000.md/RCS/timerMC.c,v 1.2 91/03/05 15:11:28 jhh Exp $ SPRITE (Berkeley)";
  43. #endif
  44.  
  45. #include <sprite.h>
  46. #include <sys.h>
  47. #include <timerInt.h>
  48. #include <timerTick.h>
  49. #include <spriteTime.h>
  50. #include <mach.h>
  51. #include <machMon.h>
  52. #include <prof.h>
  53. #include <timer.h>
  54. #include <machAddrs.h>
  55. #include <assert.h>
  56.  
  57. /*
  58.  * Control register A.
  59.  */
  60. #define REGA_UIP        0x80
  61. #define REGA_TIME_DIV        0x70
  62. #define REGA_RATE_SELECT    0x0F
  63.  
  64. /*
  65.  * Time base to use in the REGA_TIME_DIV field.
  66.  */
  67. #define REGA_TIME_BASE        0x20
  68.  
  69. /*
  70.  * Set the interval at 7.8125 ms.  RATE_US is the number of microseconds
  71.  * to add to the counter at each interrupt.  WHEN_TO_ADD_ONE says after
  72.  * how many intervals should one extra microsecond be added in.  This
  73.  * is necessary because the interval is actually 7812.5 microseconds.
  74.  */
  75. #define SELECTED_RATE        0x9
  76. #define RATE_US            7812
  77. #define WHEN_TO_ADD_ONE        0x1
  78.  
  79. /*
  80.  * Set up the interval structures. These are passed to Timer_CallBack
  81.  * and are used to compute the interval between callbacks. Since we have
  82.  * an interval of 7812.5 on the stupid ds3100 we have two structures and
  83.  * alternate passing them to Timer_CallBack.
  84.  * The interval is represented both as a Time, and as an integer. It is
  85.  * too expensive to convert between them later so set them both up here.
  86.  */
  87.  
  88.  
  89.  
  90. static Time lowTime = {0, 7812};
  91. static int lowInterval = 7812;
  92. static Time highTime = {0, 7813};
  93. static int highInterval = 7813;
  94.  
  95. /*
  96.  * Control register B.
  97.  */
  98. #define REGB_SET_TIME        0x80
  99. #define REGB_PER_INT_ENA    0x40
  100. #define REGB_UPDATE_INT_ENA    0x10
  101. #define REGB_DATA_MODE        0x04
  102. #define REGB_HOURS_FORMAT    0x02
  103.  
  104. /*
  105.  * Control register C.
  106.  */
  107. #define REGC_INT_PENDING    0x80
  108. #define REGC_PER_INT_PENDING    0x40
  109. #define REGC_UPDATE_INT_PENDING    0x10
  110.  
  111. /*
  112.  * Control register D.
  113.  */
  114.  
  115. #define REGD_VALID_TIME        0x80
  116. /*
  117.  * Pointers to registers. 
  118.  */
  119. volatile unsigned char    *secPtr  = (unsigned char *)(MACH_RTC_ADDR + 0x00);
  120. volatile unsigned char    *minPtr  = (unsigned char *)(MACH_RTC_ADDR + 0x08);
  121. volatile unsigned char    *hourPtr = (unsigned char *)(MACH_RTC_ADDR + 0x10);
  122. volatile unsigned char    *dayPtr  = (unsigned char *)(MACH_RTC_ADDR + 0x1C);
  123. volatile unsigned char    *monPtr  = (unsigned char *)(MACH_RTC_ADDR + 0x20);
  124. volatile unsigned char    *yearPtr = (unsigned char *)(MACH_RTC_ADDR + 0x24);
  125. volatile unsigned char    *regAPtr = (unsigned char *)(MACH_RTC_ADDR + 0x28);
  126. volatile unsigned char    *regBPtr = (unsigned char *)(MACH_RTC_ADDR + 0x2C);
  127. volatile unsigned char    *regCPtr = (unsigned char *)(MACH_RTC_ADDR + 0x30);
  128. volatile unsigned char    *regDPtr = (unsigned char *)(MACH_RTC_ADDR + 0x34);
  129.  
  130. /*
  131.  * We store a couple of things in the non-volatile ram.
  132.  */
  133.  
  134. #define NVR_ADDR (MACH_RTC_ADDR + 0x38)
  135.  
  136. volatile unsigned char *localOffsetNVMPtr = (unsigned char *) (NVR_ADDR + 0x00);
  137. volatile unsigned char *DSTAllowNVMPtr    = (unsigned char *) (NVR_ADDR + 0x04);
  138.  
  139. #define ONE_MILLION    1000000
  140.  
  141. /*
  142.  * The "free running counter"
  143.  */
  144. Timer_Ticks counter;
  145.  
  146. Boolean    callbackIntrsWanted = FALSE;
  147. Boolean profileIntrsWanted = FALSE;
  148.  
  149.  
  150. /*
  151.  * The RTC registers can only be accessed one byte at a time. This routine
  152.  * is used to write words into the non-volatile storage.
  153.  */
  154.  
  155. #define BYTECOPY(a,b,num) { \
  156.     int    i; \
  157.     for (i = 0; i < (num); i++) { \
  158.     ((char *) (b))[i] = ((char *) (a))[i]; \
  159.     } \
  160. }
  161.  
  162. /*
  163.  * Used for debugging. Counts number of times free-running counter was
  164.  * corrected.
  165.  */
  166. int     timerCorrectedClock;
  167.  
  168. void    Timer_TimerServiceInterrupt _ARGS_((unsigned int statusReg, 
  169.         unsigned int causeReg, Address pc, ClientData data));
  170.  
  171. /*
  172.  *----------------------------------------------------------------------
  173.  *
  174.  * Timer_TimerInit --
  175.  *
  176.  *    Initialize the periodic timer.
  177.  *
  178.  *    N.B. This routine must be called before Timer_TimerStart.
  179.  *
  180.  * Results:
  181.  *    None.
  182.  *
  183.  * Side effects:
  184.  *    The timer is initialized.
  185.  *
  186.  *----------------------------------------------------------------------
  187.  */
  188. /*ARGSUSED*/
  189. void
  190. Timer_TimerInit(timer)
  191.     unsigned short     timer;
  192. {
  193.     *regAPtr = REGA_TIME_BASE | SELECTED_RATE;
  194.     *regBPtr = REGB_DATA_MODE | REGB_HOURS_FORMAT;
  195.     Mach_SetHandler(MACH_RTC_INTR, Timer_TimerServiceInterrupt, 
  196.         (ClientData) NIL);
  197. }
  198.  
  199.  
  200. /*
  201.  *----------------------------------------------------------------------
  202.  *
  203.  * Timer_TimerStart --
  204.  *
  205.  *    Start the timer ticking.
  206.  *    ands starts the timer.
  207.  *
  208.  *    N.B. The timer must have been initialized with Timer_TimerInit
  209.  *    before this routine is called.
  210.  *
  211.  * Results:
  212.  *    None.
  213.  *
  214.  * Side effects:
  215.  *    The timer starts ticking.
  216.  *
  217.  *----------------------------------------------------------------------
  218.  */
  219. void
  220. Timer_TimerStart(timer)
  221.     register unsigned short timer;
  222. {
  223. #ifndef lint
  224.     unsigned char    dummy;
  225. #endif
  226.  
  227.     *regBPtr |= REGB_PER_INT_ENA;
  228. #ifndef lint
  229.     dummy = *regCPtr;
  230. #endif
  231.  
  232.     Mach_MonPrintf("Starting timer interrupts.\n");
  233.     if (timer == TIMER_CALLBACK_TIMER) {
  234.     callbackIntrsWanted = TRUE;
  235.     } else if (timer == TIMER_PROFILE_TIMER) {
  236.     profileIntrsWanted = TRUE;
  237.     } else {
  238.     panic("Timer_TimerStart: unknown timer %d\n", timer);
  239.     }
  240. }
  241.  
  242.  
  243. /*
  244.  *----------------------------------------------------------------------
  245.  *
  246.  * Timer_TimerInactivate --
  247.  *
  248.  *      Stops the specified timer such that it will cease counting and
  249.  *      also resests the mode register to 0.  If the timer has already
  250.  *      stopped and has set its output line high, clear the output so it
  251.  *      won't cause an interrupt (because we don't care that it has
  252.  *      expired).
  253.  *
  254.  * Results:
  255.  *    None.
  256.  *
  257.  * Side effects:
  258.  *    The timer is stopped.
  259.  *
  260.  *----------------------------------------------------------------------
  261.  */
  262. void
  263. Timer_TimerInactivate(timer)
  264.     register unsigned short timer;
  265. {
  266.     if (timer == TIMER_CALLBACK_TIMER) {
  267.     callbackIntrsWanted = FALSE;
  268.     } else if (timer == TIMER_PROFILE_TIMER) {
  269.     profileIntrsWanted = FALSE;
  270.     } else {
  271.     panic("Timer_TimerInactivate: unknown timer %d\n", timer);
  272.     }
  273.  
  274.     /*
  275.      * If neither type of timer interrupt is wanted, then disable
  276.      * timer interrupts.
  277.      */
  278.     if (!callbackIntrsWanted && !profileIntrsWanted) {
  279.     *regBPtr &= ~REGB_PER_INT_ENA;
  280.     }
  281. }
  282.  
  283.  
  284. /*
  285.  *----------------------------------------------------------------------
  286.  *
  287.  *  Timer_TimerServiceInterrupt --
  288.  *
  289.  *      This routine is called at every timer interrupt. 
  290.  *      It calls the timer callback queue handling if the callback timer 
  291.  *    expired and calls the profiling interrupt handling if the 
  292.  *    profile callback timer expired.
  293.  *
  294.  *  Results:
  295.  *    None.
  296.  *
  297.  *  Side Effects:
  298.  *    Routines on the timer queue may cause side effects. Profile
  299.  *    collect may take place. 
  300.  *    
  301.  *
  302.  *----------------------------------------------------------------------
  303.  */
  304.  
  305. Address    timer_pc;
  306.  
  307. void
  308. Timer_TimerServiceInterrupt(statusReg, causeReg, pc, data)
  309.     unsigned int    statusReg;
  310.     unsigned int    causeReg;
  311.     Address         pc;
  312.     ClientData      data;
  313.     static unsigned    addOne = 0;
  314.     unsigned char     timerStatus;
  315.     Time        *timePtr;
  316.     unsigned int     interval;
  317.     Time_Parts        currentTODParts;
  318.     static int        previousSoftSeconds;
  319.     static int        previousHardSeconds;
  320.     int            softSeconds;
  321.     int            hardSeconds;
  322.     int            diff;
  323.     static Boolean    initialized = FALSE;
  324.  
  325.     static    int    eventDebug[1000];
  326.     static    int    dbgCtr = 0;
  327. #define INC(a) { (a) = ((a) + 1) % 1000; }
  328.  
  329.     timer_pc = pc;
  330.     timerStatus = *regCPtr;
  331.     eventDebug[dbgCtr] = 0;
  332.     if (timerStatus & REGC_PER_INT_PENDING) {
  333.     /*
  334.      * Increment the counter.
  335.      */
  336.     counter.microseconds += RATE_US;
  337.     if ((addOne & WHEN_TO_ADD_ONE) == WHEN_TO_ADD_ONE) {
  338.         counter.microseconds++;
  339.         timePtr = &highTime;
  340.         interval = highInterval;
  341.     } else {
  342.         timePtr = &lowTime;
  343.         interval = lowInterval;
  344.     }
  345.     addOne++;
  346.     if (counter.microseconds >= ONE_MILLION) {
  347.         /*
  348.          * We wrapped around. 
  349.          */
  350.         counter.microseconds = ONE_MILLION - 1;
  351.     }
  352.     }
  353.     if ((timerStatus & REGC_UPDATE_INT_PENDING)) {
  354.     /*
  355.      * RTC interrupt.
  356.      */
  357.     counter.seconds++;
  358.     counter.microseconds = 0;
  359.     eventDebug[dbgCtr] |= 0x1;
  360.     if ((*regAPtr & REGA_UIP) == 0) {
  361.         currentTODParts.seconds = *secPtr;
  362.         currentTODParts.minutes = *minPtr;
  363.         currentTODParts.hours = *hourPtr;
  364.         currentTODParts.dayOfMonth = *dayPtr;
  365.         currentTODParts.dayOfYear = -1;
  366.         currentTODParts.month = *monPtr-1;
  367.         currentTODParts.year = *yearPtr;
  368.         (void) Time_FromParts(¤tTODParts, FALSE, &hardSeconds);
  369.         softSeconds = counter.seconds;
  370.         diff = hardSeconds - previousHardSeconds;
  371.         eventDebug[dbgCtr] |= 0x4;
  372.         if (softSeconds - previousSoftSeconds != diff && initialized) {
  373.         /*
  374.          * Note that the software counter cannot be ahead of
  375.          * the hardware counter. We are only looking at the
  376.          * seconds, and the seconds are only incremented 
  377.          * during an interrupt. We only get one interrupt
  378.          * a second so we may be behind but not ahead.
  379.          */
  380.         if (softSeconds - previousSoftSeconds > diff) {
  381.             panic("Software time is ahead of hardware!\n");
  382.         }
  383.         counter.seconds = previousSoftSeconds + diff;
  384.         softSeconds = counter.seconds;
  385.         timerCorrectedClock++;
  386.         eventDebug[dbgCtr] |= 0x8;
  387.         }
  388.         previousHardSeconds = hardSeconds;
  389.         previousSoftSeconds = softSeconds;
  390.         initialized = TRUE;
  391.     }
  392.     }    
  393.     if (timerStatus & REGC_PER_INT_PENDING) {
  394.     if (mach_KernelMode) {
  395.         assert((statusReg & MACH_SR_KU_PREV) == 0);
  396.         /*
  397.          * Check for kernel profiling.  We'll sample the PC here.
  398.          */
  399.         if (profileIntrsWanted) {
  400.         TIMER_PROFILE_ROUTINE(pc);
  401.         } 
  402.     } else {
  403.         assert((statusReg & MACH_SR_KU_PREV) != 0);
  404.         Proc_GetCurrentProc()->Prof_PC = (int) pc;
  405.     }
  406.     TIMER_CALLBACK_ROUTINE(interval, *timePtr);
  407.     }
  408.     INC(dbgCtr);
  409. }
  410.  
  411.  
  412. /*
  413.  *----------------------------------------------------------------------
  414.  *
  415.  * Timer_CounterInit --
  416.  *
  417.  *    Initialize free-running counter.
  418.  *
  419.  * Results:
  420.  *    None.
  421.  *
  422.  * Side effects:
  423.  *    The specified counters begin to count.
  424.  *
  425.  *----------------------------------------------------------------------
  426.  */
  427. void
  428. Timer_CounterInit()
  429. {
  430.     counter.seconds = 0;
  431.     counter.microseconds = 0;
  432. }
  433.  
  434.  
  435. /*
  436.  *----------------------------------------------------------------------
  437.  *
  438.  *  Timer_GetCurrentTicks --
  439.  *
  440.  *      Return microseconds since boot.
  441.  *
  442.  *  Results:
  443.  *    The system up-time in ticks.
  444.  *
  445.  *  Side effects:
  446.  *    None.
  447.  *
  448.  *----------------------------------------------------------------------
  449.  */
  450. void
  451. Timer_GetCurrentTicks(ticksPtr)
  452.     Timer_Ticks    *ticksPtr;    /* Buffer to place current time. */
  453. {
  454.     DISABLE_INTR();
  455.  
  456.     *ticksPtr = counter;
  457.  
  458.     ENABLE_INTR();
  459. }
  460.  
  461.  
  462. /*
  463.  *----------------------------------------------------------------------
  464.  *
  465.  *  Timer_GetInfo --
  466.  *
  467.  *      Dummy routine to dump timer state.
  468.  *
  469.  *  Results:
  470.  *    None.
  471.  *
  472.  *  Side effects:
  473.  *    None.
  474.  *
  475.  *----------------------------------------------------------------------
  476.  */
  477. void
  478. Timer_TimerGetInfo()
  479. {
  480. }
  481.  
  482. /*
  483.  *----------------------------------------------------------------------
  484.  *
  485.  * TimerHardwareUniversalTimeInit --
  486.  *
  487.  *    Checks the battery backed up clock. If the contents are still
  488.  *    valid then we return them. 
  489.  *
  490.  *    IMPORTANT:  right now it looks like the ds3100 RTC is reset
  491.  *    by the prom during the boot.
  492.  *
  493.  * Results:
  494.  *    None.
  495.  *
  496.  * Side effects:
  497.  *    None.
  498.  *
  499.  *----------------------------------------------------------------------
  500.  */
  501.  
  502. void
  503. TimerHardwareUniversalTimeInit(timePtr, localOffsetPtr, DSTPtr)
  504.     Time *timePtr;        /* Buffer to hold universal time. */
  505.     int  *localOffsetPtr;    /* Buffer to hold local offset. */
  506.     Boolean *DSTPtr;        /* Buffer to hold DST allowed flag. */
  507. {
  508.     Time_Parts        timeParts;
  509.     int            seconds;
  510.     ReturnStatus    status;
  511.  
  512.     while ((*regAPtr & REGA_UIP) == 1) {
  513.     }
  514.     timeParts.seconds = *secPtr;
  515.     timeParts.minutes = *minPtr;
  516.     timeParts.hours = *hourPtr;
  517.     timeParts.dayOfMonth = *dayPtr;
  518.     timeParts.dayOfYear = -1;
  519.     timeParts.month = *monPtr-1;
  520.     timeParts.year = *yearPtr;
  521.     status = Time_FromParts(&timeParts, FALSE, &seconds);
  522.     if (status != SUCCESS) {
  523.     Mach_MonPrintf("Time stored in RTC is bogus.\n");
  524.     return;
  525.     }
  526.     if (*regDPtr & REGD_VALID_TIME == 0) {
  527.     Mach_MonPrintf(
  528.         "Warning: battery backed up TOD clock is invalid.\n");
  529.     return;
  530.     }
  531.     bzero((Address) timePtr, sizeof(Time));
  532.     timePtr->seconds = seconds;
  533.     BYTECOPY(localOffsetNVMPtr, localOffsetPtr, 4);
  534.     BYTECOPY(DSTAllowNVMPtr, DSTPtr, 4);
  535. }
  536.  
  537. /*
  538.  *----------------------------------------------------------------------
  539.  *
  540.  * TimerSetHardwareUniversalTime --
  541.  *
  542.  *    Sets the hardware RTC clock. Has to be called with interrupts
  543.  *    off.
  544.  *
  545.  *    IMPORTANT: The RTC is used to keep the free-running counter
  546.  *    up-to-date if we miss a few interrupts. DO NOT RESET THE RTC
  547.  *    WHILE THE KERNEL IS RUNNING!!! If you do system time will
  548.  *    not be linear and bad things will happen. Only call this routine
  549.  *    during startup and shutdown. As things now stand there is no
  550.  *    reason to call this routine at all since the RTC does not
  551.  *    appear to persist across reboots.
  552.  *
  553.  * Results:
  554.  *    None.
  555.  *
  556.  * Side effects:
  557.  *    The hardware RTC is set.
  558.  *
  559.  *----------------------------------------------------------------------
  560.  */
  561.  
  562. void
  563. TimerSetHardwareUniversalTime(timePtr, localOffset, DST)
  564.     Time *timePtr;        /* universal time. */
  565.     int  localOffset;        /* local offset. */
  566.     Boolean DST;        /* DST allowed flag. */
  567. {
  568.     Time_Parts        timeParts;
  569.  
  570.     Time_ToParts(timePtr->seconds, FALSE, &timeParts);
  571.     *regBPtr |= REGB_SET_TIME;
  572.     *secPtr = (unsigned char) timeParts.seconds;
  573.     *minPtr = (unsigned char) timeParts.minutes;
  574.     *hourPtr = (unsigned char) timeParts.hours;
  575.     *dayPtr = (unsigned char) timeParts.dayOfMonth;
  576.     *monPtr = (unsigned char) timeParts.month+1;
  577.     *yearPtr = (unsigned char) timeParts.year;
  578.     BYTECOPY(&localOffset,localOffsetNVMPtr,  4);
  579.     BYTECOPY(&DST, DSTAllowNVMPtr, 4);
  580.     *regBPtr &= ~REGB_SET_TIME;
  581. }
  582.